#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
 
#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"
#include "user_define.h"
#include "record_define.h"
#include "mac_label.h"
#include "score_compute.h"
// add para lib_include
static NAME2VALUE position_level[10] = {
	{"ER",1},
	{"PM",2},
	{"DM",3},
	{"CTO",4},
	{"CFO",4},
	{"CAO",4},
	{"CMO",4},
	{"CEO",5},
	{NULL,0},
}; 
int event_judge_init(void * sub_proc, void * para)
{
	int ret;
	// add yorself's module init func here
	return 0;
}

int event_judge_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	// add yorself's module exec func here
	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if(!memdb_find_recordtype(type,subtype))
		{
			printf("message format (%d %d) is not registered!\n",
			message_get_type(recv_msg),message_get_subtype(recv_msg));
			continue;
		}
		else if((type==TYPE(RECORD_DEFINE))&&(subtype==SUBTYPE(RECORD_DEFINE,RECORD)))
		{
			ret=proc_event_judge(sub_proc,recv_msg);
		}
	}
	return 0;
}
int get_level(char* position)
{
	int i;
	for(i=0;position_level[i].name!=NULL;i++)
	{
		if(Strcmp(position,position_level[i].name) == 0)
			return position_level[i].value;
	}
	return 0;
}
enum flag_sec_class convert(char* department)
{
	switch(department[0]){
		case 'R':
			return R;
		case 'F':
			return F;
		case 'M':
			return M;
		case 'A':
			return A;
		default:
			return 0;
	}
}
int read_acl(RECORD(MAC_LABEL,SUBJECT) * sub_label,RECORD(MAC_LABEL,OBJECT) * obj_label)
{
//  add blp module access control code here
//  return 1 means access is permitted
	if ((sub_label->label.sec_class | obj_label->label.sec_class) == sub_label->label.sec_class)
    	if (sub_label->label.sec_label >= obj_label->label.sec_label)
      		return 1;

	return 0;	
}

RECORD(MAC_LABEL,OBJECT) * compute_objlabel(RECORD(RECORD_DEFINE,RECORD) * record_data,
		RECORD(MAC_LABEL,RECORD) * record_label)
	// 根据数据内容和记录项安全标记策略，生成数据的客体标记
{
	RECORD(MAC_LABEL,OBJECT) * obj_label = Talloc0(sizeof(struct mac_label_object));
	obj_label->user_name = dup_str(record_data->name,DIGEST_SIZE);

	Strncpy(obj_label->user_id,record_data->ID,sizeof(record_data->ID));
	Strncpy(obj_label->record_name,record_label->name,sizeof(record_label->name));

	if(record_label->isselfdefine == 1){
		obj_label->label.sec_class = record_label->class;
	}else{
		obj_label->label.sec_class = convert(record_data->department);
		
	}

	if(record_label->isleveladjust == 1){
		obj_label->label.sec_label = record_label->level_adjust + get_level((record_data->position));
	}else{
		obj_label->label.sec_label = record_label->level_fix;
	}

	return obj_label;
}

int proc_event_judge(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(RECORD_DEFINE,RECORD) * record_data;
	RECORD(RECORD_DEFINE,RECORD) * compare_record;
	RECORD(USER_DEFINE,CLIENT_STATE) * user_state;
	RECORD(MAC_LABEL,SUBJECT) * user_label;
	RECORD(MAC_LABEL,OBJECT) * object_label;
	RECORD(MAC_LABEL,RECORD) * record_label;
	MSG_EXPAND * msg_expand;
	DB_RECORD * db_record;
	void * new_msg;
	ELEM_ATTR * elem_attr;
	int i;
	int elem_no;
	void * record_template;


	//获取已完成访控处理的数据 
	ret=message_get_record(recv_msg,&record_data,0);
	if(ret<0)
		return ret;

	//获取本次访问的客户端状态
	ret=message_remove_expand(recv_msg,TYPE_PAIR(USER_DEFINE,CLIENT_STATE),&msg_expand);
	if(ret<0)
		return ret;
	if(msg_expand==NULL)
	{
		print_cubeerr("can't find user attached!\n");	
		return -EINVAL;
	}
	user_state=msg_expand->expand;
	// 根据用户名获取主体标记
	db_record=memdb_find_first(TYPE_PAIR(MAC_LABEL,SUBJECT),"user_name",user_state->user_name);
	if(db_record==NULL)
	{
		print_cubeerr("user_label: can't find label!\n");
		return -EINVAL;
	}
	
	user_label=db_record->record;

	// 获取对照的客体数值
	db_record=memdb_find_first(TYPE_PAIR(RECORD_DEFINE,RECORD),"name",record_data->name);
	if(db_record==NULL)
	{
		return -EINVAL;
	}
	else
	{
		compare_record=db_record->record;
	}

	/// 进行裁决，具体为遍历所有记录项，检查访问控制策略，如果允许，则数据应当一致，
	//  如禁止则record_data相关项应为空
		
	record_template = memdb_get_template(TYPE_PAIR(RECORD_DEFINE,RECORD));
	if(record_template == NULL)
		return -EINVAL;
	elem_no = struct_get_elem_no(record_template);
	int event_result=1; // 1 means correct
	                   // -1 means read sth should  not be read
			   // -2 means can't read sth should be read
			   // -3 means read self data failed
	for(i=0;i<elem_no;i++)
	{
		int comp_result;
		//获取第i项的记录项描述信息
		elem_attr = struct_get_elem(record_template,i);
		//获取客体标记，分二步，第一步,获取记录项的客体属性

		db_record=memdb_find_first(TYPE_PAIR(MAC_LABEL,RECORD),"name",elem_attr->name);
		if(db_record==NULL)
		{
			print_cubeerr("record_label: can't find %s!\n",elem_attr->name);
			continue;
		}
	
		record_label=db_record->record;
		
		//获取客体标记第二步,使用用户信息和客体属性来计算客体标记
		// printf(" record  label value: %d %d\n",record_label->isleveladjust,record_label->level_adjust);
	 
        	object_label = compute_objlabel(record_data,record_label);	
		//根据主体标记和客体标记执行访问控制
		// printf(" obj label value: %x %d\n",object_label->label.sec_class,object_label->label.sec_label);
		ret = read_acl(user_label,object_label);

		//根据访问控制结果检查记录项,如访控结果为1,则对比record_data是否与compare_record一致，
		//如不一致，则event_result返回-2
		if(ret)
		{
			comp_result=struct_comp_elem(elem_attr->name,record_data,
				compare_record,record_template);
			if(comp_result==0)
			{
				event_result=-2;
				break;
			}
			continue;
		}
		// 例外原则：如访控规则不一致，当记录项为"salary"时,如果主体客体为同一用户，
		// 则应允许读取，否则读取失败
		if(Strcmp("salary",elem_attr->name)==0)
		{
			if(struct_comp_elem_value("name",record_data,user_state->user_name,record_template)==1)
			{
				// 主体客体为同一用户
				if(struct_comp_elem("salary",record_data,compare_record,record_template)==0)
				{
					event_result=-3;
					break;
				}
				continue;
			}
		}
	
	
		//如访控结果为0,则对比record_data相关项是否为空,不空则返回-1
		switch(elem_attr->type)	
		{	
			case CUBE_TYPE_STRING:
				{
					char file_string[DIGEST_SIZE];
					Memset(file_string,'*',elem_attr->size);
					comp_result=struct_comp_elem_value(elem_attr->name,record_data,
						file_string,record_template);
					if(comp_result==0)
					{
						event_result=-1;
						break;
					}
				}
				break;
			case CUBE_TYPE_ESTRING:
				{
					char file_string[DIGEST_SIZE];
					file_string[0]='\0';
					comp_result=struct_comp_elem_value(elem_attr->name,record_data,
						file_string,record_template);
					if(comp_result==0)
					{
						event_result=-1;
						break;
					}
				}
				break;
			case CUBE_TYPE_INT:
				{	
					int value=0;
					comp_result=struct_comp_elem_value(elem_attr->name,record_data,
						&value,record_template);
					if(comp_result==0)
					{
						event_result=-1;
						break;
					}
				}
				break;
			default:
				return -EINVAL;
		}
	}
	RECORD(SCORE_COMPUTE,EVENT) * score_event;
	score_event=Talloc0(sizeof(*score_event));
	if(score_event == NULL)
		return -ENOMEM;

	switch(event_result)
	{
		case 1:
			score_event->name = dup_str("success read",0);
			score_event->item_name = dup_str("read_record",0);
	       		score_event->result=SCORE_RESULT_SUCCEED;	
			break;
		case -1:	
			score_event->name = dup_str("secret leak",0);
			score_event->item_name = dup_str("read_record",0);
	       		score_event->result=SCORE_RESULT_FAIL;	
			break;
		case -2:	
			score_event->name = dup_str("leagl read fail",0);
			score_event->item_name = dup_str("read_record",0);
	       		score_event->result=SCORE_RESULT_FAIL;	
			break;
		case -3:	
			score_event->name = dup_str("trust read fail",0);
			score_event->item_name = dup_str("read_record",0);
	       		score_event->result=SCORE_RESULT_FAIL;	
			break;
		default:
			score_event->name = dup_str("unknown error",0);
			score_event->item_name = dup_str("read_record",0);
	       		score_event->result=SCORE_RESULT_FAIL;	
			break;
	}
	new_msg=message_create(TYPE_PAIR(SCORE_COMPUTE,EVENT),NULL);
	if(new_msg==NULL)
		return -EINVAL;
	message_add_record(new_msg,score_event);
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}
